(function () {
    "use strict";
    /* 工具类方法 */
    // Define Non Enumerable Properties
    var define = function define(obj, key, value) {
        Object.defineProperty(obj, key, {
            writable: true,
            configurable: true,
            enumerable: false,
            value: value
        });
    };
    // thenable : validate x is a promiseA+ compliant instance
    var thenable = function thenable(x) {
        if (x !== null && /^(object|function)$/i.test(typeof x)) {
            var then;
            try {
                then = x.then;
            } catch (err) {
                return false;
            }
            if (typeof then === "function") return true;
        }
        return false;
    };

    /* 核心操作 */
    function Promise(executor) {
        var self = this,
            change;
        // Validate Params
        if (typeof executor !== "function") throw new TypeError("Promise resolver is not a function");
        if (!(self instanceof Promise)) throw new TypeError("Promise constructor cannot be invoked without 'new'");

        // Initial Private Property
        self.state = "pending";
        self.result = undefined;
        self.onfulfilledCallbacks = [];
        self.onrejectedCallbacks = [];
        change = function change(state, result) {
            if (self.state !== "pending") return;
            self.state = state;
            self.result = result;
            // Method Execution In Notification Collection
            var callbacks = state === "fulfilled" ? self.onfulfilledCallbacks : self.onrejectedCallbacks;
            setTimeout(function () {
                callbacks.forEach(function (callback) {
                    if (typeof callback === "function") {
                        callback(result);
                    }
                });
            });
        };

        // Execute Now executor
        try {
            executor(function resolve(value) {
                change("fulfilled", value);
            }, function reject(reason) {
                change("rejected", reason);
            });
        } catch (err) {
            change("rejected", err);
        }
    }

    /* Promise的原型对象 */
    var proto = Promise.prototype;
    var ResolvePromise = function ResolvePromise(promise, x, resolve, reject) {
        if (x === promise) throw new TypeError("Chaining cycle detected for promise #<Promise>");
        if (x !== null && /^(object|function)$/i.test(typeof x)) {
            var then;
            try {
                then = x.then;
            } catch (err) {
                reject(err);
                return;
            }
            if (typeof then === "function") {
                // x is a promiseA+ compliant instance
                var called;
                try {
                    then.call(
                        x,
                        function onfulfilled(y) {
                            if (called) return;
                            called = true;
                            ResolvePromise(promise, y, resolve, reject);
                        },
                        function onrejected(r) {
                            if (called) return;
                            called = true;
                            reject(r);
                        }
                    );
                } catch (err) {
                    if (called) return;
                    called = true;
                    reject(err);
                }
                return;
            }
        }
        resolve(x);
    };
    if (typeof Symbol !== "undefined") define(proto, Symbol.toStringTag, "Promise");
    define(proto, "then", function then(onfulfilled, onrejected) {
        var self = this,
            promise;
        if (!(self instanceof Promise)) throw new TypeError("Method Promise.prototype.then called on incompatible receiver 10");

        // then through
        if (typeof onfulfilled !== "function") {
            onfulfilled = function onfulfilled(value) {
                return value;
            };
        }
        if (typeof onrejected !== "function") {
            onrejected = function onrejected(reason) {
                throw reason;
            };
        }

        // create new promise instance
        promise = new Promise(function (resolve, reject) {
            switch (self.state) {
                case "fulfilled":
                    setTimeout(function () {
                        try {
                            var x = onfulfilled(self.result);
                            ResolvePromise(promise, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                    break;
                case "rejected":
                    setTimeout(function () {
                        try {
                            var x = onrejected(self.result);
                            ResolvePromise(promise, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                    break;
                default:
                    self.onfulfilledCallbacks.push(function (value) {
                        try {
                            var x = onfulfilled(value);
                            ResolvePromise(promise, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                    self.onrejectedCallbacks.push(function (reason) {
                        try {
                            var x = onrejected(reason);
                            ResolvePromise(promise, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
            }
        });
        return promise;
    });
    define(proto, "catch", function mycatch(onrejected) {
        var self = this;
        if (!(self instanceof Promise)) throw new TypeError("Method Promise.prototype.then called on incompatible receiver 10");
        return self.then(null, onrejected);
    });

    /* 静态私有属性方法 */
    define(Promise, "resolve", function resolve(value) {
        if (thenable(value)) return value;
        return new Promise(function (resolve) {
            resolve(value);
        });
    });
    define(Promise, "reject", function reject(reason) {
        return new Promise(function (_, reject) {
            reject(reason);
        });
    });
    define(Promise, "all", function all(promises) {
        if (!Array.isArray(promises)) throw new TypeError("promises is not an array");
        return new Promise(function (resolve, reject) {
            var values = [],
                n = 0;
            promises.forEach(function (promise, index) {
                if (!thenable(promise)) promise = Promise.resolve(promise);
                promise.then(
                    function onfulfilled(value) {
                        n++;
                        values[index] = value;
                        // all fulfilled
                        if (n >= promises.length) resolve(values);
                    },
                    function onrejected(reason) {
                        // There is one failure, the whole is failure
                        reject(reason);
                    }
                );
            });
        });
    });
    define(Promise, "any", function any(promises) {
        if (!Array.isArray(promises)) throw new TypeError("promises is not an array");
        return new Promise(function (resolve, reject) {
            var n = 0;
            promises.forEach(function (promise) {
                if (!thenable(promise)) promise = Promise.resolve(promise);
                promise.then(
                    function onfulfilled(value) {
                        // A certain success, the whole is success
                        resolve(value);
                    },
                    function onrejected() {
                        n++;
                        // all rejected
                        if (n >= promises.length) reject(new Error("All promises were rejected"));
                    }
                );
            });
        });
    });

    /* 单元测试*/
    Promise.deferred = function () {
        var result = {};
        result.promise = new Promise(function (resolve, reject) {
            result.resolve = resolve;
            result.reject = reject;
        });
        return result;
    };

    /* 暴露API:支持浏览器环境、也支持Node环境 */
    if (typeof window !== "undefined") window.Promise = Promise;
    if (typeof module === "object" && typeof module.exports === "object") module.exports = Promise;
})();